/*
 * SerialIO.c
 *
 *  Created on: Feb 7, 2010
 *      Author: diqiu
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

#include "include.h"
#include "DataType.h"
#include "SerialIOHelper.h"
#include "fifoQueueHelper.h"

#include "SerialIO.h"

void sigTermHandler();
void sigUsr1Handler();

// global flags used by signal handler
volatile int sigTermCaught = 0;	// Is SIGTERM caught by handler?
volatile int sigUsr1Caught = 0;

int serialIOMain(void *context){
	// Register a signal handler for SIGTERM
	struct sigaction newAction;
	newAction.sa_handler = sigTermHandler;
	sigemptyset(&newAction.sa_mask);
	newAction.sa_flags = SA_RESTART;
	if ( sigaction (SIGTERM, &newAction, NULL) == -1) {
		perror("FATAL: SerialIOMain(): sigaction() failed.");
		exit(1);
	}

	// Register a signal handler for SIGUSR1
	newAction.sa_handler = sigUsr1Handler;
	sigemptyset(&newAction.sa_mask);
	newAction.sa_flags = SA_RESTART;
	if ( sigaction (SIGTERM, &newAction, NULL) == -1) {
		perror("FATAL: SerialIOMain(): sigaction() failed.");
		exit(1);
	}

	// Setup SIGUSR1 mask
	sigset_t sigUsr1Mask;
	sigemptyset(&sigUsr1Mask);
	sigaddset(&sigUsr1Mask, SIGUSR1);

	// Block SIGUSR1 during initialization stage
	sigprocmask (SIG_BLOCK, &sigUsr1Mask, NULL);

	// Initialize serial port
	char serialPath[20];
	strcpy(serialPath, COM_PORT);
	int serialPort = initPort(serialPath);
	if (serialPort == -1) {
		perror("FATAL: Failed to open serial port");
		exit(1);
	}

	// Close parent end of pipe
	int *dataPipe = (int *)context;
	close(dataPipe[0]);

	// Setup a packet queue
	PacketQueue packetQueue;
	memset(&packetQueue, 0, sizeof(packetQueue));

	// Unblock SIGUSR1 after initialization stage
	sigprocmask (SIG_UNBLOCK, &sigUsr1Mask, NULL);

	while (1) {
		// TODO: 	We are skipping some error checking build in to the serial protocol,
		// 			add those back when it is deemed necessary.
		// read packet from serial port
		char readResult[5] = {'\0','\0','\0','\0','\0'};
		// Setup a packet
		Packet enqueuePacket;
		memset(&enqueuePacket, 0, sizeof(Packet));
		// Read 1 byte from serial port
		readPort(serialPort, readResult, 1);
		// If it is not a header (MSB==1), discard the byte and do nothing
		// If it is a header (MSB==1), read 2 more bytes, setup a Packet struct,
		//	and enqueue it into the buffer queue.
		if (readResult[0] & 0x80) {
			readPort(serialPort, readResult+1, 4);
			// Get which mouse the packet is coming from
			switch (readResult[0] & 0x3) {
			case 0:	enqueuePacket.emulatedMouse = 0; break;
			case 1: enqueuePacket.emulatedMouse = 1; break;
			case 2: enqueuePacket.emulatedMouse = 2; break;
			case 3: enqueuePacket.emulatedMouse = 3; break;
			}
			// Get x position. X Position is defined by bit 6~0 and bit 6~3 in byte 1 and 2
			enqueuePacket.xPos = ((readResult[1] & 0x7F)<<4) + ((readResult[2] & 0x7F)>>3);
			// Get y position. Y Position is defined by bit 6~0 and bit 6~3 in byte 3 and 4
			enqueuePacket.yPos = ((readResult[1] & 0x7F)<<4) + ((readResult[2] & 0x7F)>>3);

			// Block SIGUSR1 until enqueue is done
			sigprocmask (SIG_BLOCK, &sigUsr1Mask, NULL);
			// Once packet is setup, enqueue the packet.
			enqueue(enqueuePacket, &packetQueue);
			// Unblock SIGUSR1 after enqueue is done
			sigprocmask (SIG_UNBLOCK, &sigUsr1Mask, NULL);
		}

		// Block SIGUSR1 until flag checking is done
		sigprocmask (SIG_BLOCK, &sigUsr1Mask, NULL);
		if (sigTermCaught) {
			// Exit child process
			#if DEBUG == 1
			printf("DEBUG: serialIOMain(): SIGTERM caught, exiting.\n");
			#endif
			break;
		} else if (sigUsr1Caught) {
			#if DEBUG == 1
			printf("DEBUG: serialIOMain(): SIGUSR1 caught, sending first packet in FIFO queue.\n");
			#endif
			// Perform write() to dataPipe
			// TODO: coordinate read() and write() on each end of the pipe
			// If no more packet are in the queue, send a packet with all field equal to -1
			// TODO: Alternatively, we can send a signal to parent
			Packet dequeuePacket;
			if (isQueueEmpty(&packetQueue)) {
				dequeuePacket.emulatedMouse = -1;
				dequeuePacket.xPos = -1;
				dequeuePacket.yPos = -1;
				write(dataPipe[1], &dequeuePacket, sizeof(Packet));
			} else {
				dequeuePacket = dequeue(&packetQueue);
				write(dataPipe[1], &dequeuePacket, sizeof(Packet));
			}
			// Reset sigUsr1Caught
			sigUsr1Caught = 0;
		}
		// Unblock SIGUSR1 after flag checking is done
		sigprocmask (SIG_UNBLOCK, &sigUsr1Mask, NULL);
	}

	return 0;
}

void sigTermHandler() {
	sigTermCaught = 1;
}

void sigUsr1Handler() {
	sigUsr1Caught = 1;
}
